<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<title>Canvas推箱子基础01</title>
		<style>
			#mycanv {
				background-color: #369;
			}

			#nav {
				width: 800px;
				height: 40px;
				text-align: center;
			}

			#g-level {
				width: 30px;
				height: 23px;
				text-align: center;
			}

			.g-btn {
				vertical-align: middle;
				width: 100px;
				height: 30px;
			}

			.btn1 {
				width: 40px;
			}
		</style>
		<script src="Boxdata100.js"></script>
	</head>

	<body>
		<div id="nav">
			<button class="g-btn" onclick="select_level(-1)">上一关</button>
			<button class="g-btn" onclick="select_level(1)">下一关</button>
			<input id="g-level" type="text" value="0" />
			<button class="g-btn btn1" onclick="select_level()">GO</button>
			<button class="g-btn" onclick="select_level(0)">重新开始</button>
		</div>
		<canvas id="mycanv" width="800" height="800"></canvas>
		<script>
			/**@type{HTMLCanvasElement}*/  //vsCode
			var canv = document.getElementById('mycanv');
			var ctx = canv.getContext('2d');
			var g_level = document.getElementById('g-level');
			// 推箱子游戏:
			//1. 基础版: 预设游戏等级,完成基本功能
			//2. 完整版: 游戏等级导航, 上一关,下一关,第几关,重新开始
			//3. 封装:面向对象
			//解析地图,获取游戏信息
			var level = 0;
			var g = {
				wall: {color: '#333',data: [],},
				target: {color: '#6a3',data: [],},
				box: {color: 'rgba(255,20,20,0.6)',data: [],},
				player: {color: 'rgba(255,255,10,0.8)',data: [],},
			}; //g.wall   g['wall']
			var keys = ['wall', 'target', 'box', 'player'];
			//遍历游戏地图,获取游戏信息
			g_init(0);
			function g_init(lv) {
				let map = levels[lv];
				keys.forEach(k => (g[k].data = []));
				for (let i = 0; i < 16; i++) {
					for (let j = 0; j < 16; j++) {
						let data = map[i][j];
						let id = i * 16 + j;
						if (data > 0) {
							let key = keys[data - 1];
							if (data == 5) {
								g['target'].data.push(id);
								g['box'].data.push(id);
							} else {
								g[key].data.push(id);
							}
						}
					}
				}
				renderMap();
			}

			function renderMap() {
				ctx.clearRect(0, 0, 800, 800);
				keys.forEach(k => g[k].data.forEach(n => drawRRect(k, n)));
			}

			function select_level(n) {
				if(n==0){
					g_init(level);
					return
				}
				let lv = n == undefined ? g_level.value * 1 : level + n;
				level = lv < 0 ? 0 : lv > 99 ? 99 : lv;
				g_level.value = level;
					g_init(level);
			}
			//绘制圆角矩形
			function drawRRect(who, id) {
				let w = 800 / 16,
					r = 8,
					m = 1; //width, radius, magin
				let [x, y] = [(id % 16) * w, ~~(id / 16) * w];
				if (who == 'target') {
					r = 20;
					m = 5;
				}
				ctx.save();
				ctx.beginPath();
				ctx.moveTo(x + m, y + m + r);
				ctx.arcTo(x + m, y + w - m, x + w - m, y + w - m, r);
				ctx.arcTo(x + w - m, y + w - m, x + w - m, y, r);
				ctx.arcTo(x + w - m, y, x + m, y + m, r);
				ctx.arcTo(x + m, y + m, x + m, y + w - m, r);
				ctx.closePath()
				ctx.fillStyle = g[who].color;
				ctx.fill();
				ctx.restore();
			}
			//键盘事件
			document.addEventListener('keydown', ev => {
				let keyCode=ev.keyCode
				if([27,37,38,39,40].includes(keyCode)){
					if(keyCode==27){
						select_level(0);
						return
					}
					//设置移动方向
					let dir = [-1, -16, 1, 16][keyCode - 37];
					let player = g.player.data[0];
					//判断撞墙
					let next = player + dir;
					if (g.wall.data.includes(next)) return;
					if (g.box.data.includes(next)) {
						let box = next;
						let bnext = box + dir;
						//判断箱子前面是否为墙或者箱子
						if (g.wall.data.includes(bnext) || g.box.data.includes(bnext)) return;
						//推箱子
						move('box', box, dir);
						//判断输赢(游戏结束)
						checkOver();
					}
					//进行移动:player
					move('player', player, dir);

				}
			});
			//移动角色
			function move(who, id, dir) {
				//根据移动方向,找到下一个位置:next
				let next = id + dir;
				//更新who角色数据
				let index = g[who].data.indexOf(id);
				g[who].data[index] = next;
				//重新渲染地图
				renderMap();
			}
			//判断输赢
			function checkOver() {
				let over = g.box.data.every(b => g.target.data.includes(b)); //true false
				if (over) {
					setTimeout(() => {
						alert('恭喜完成');
					}, 100);
				}
			}
		</script>
	</body>
</html>
